package nl.rainbowsheep.reisorg.rest.service.service;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import nl.rainbowsheep.reisorg.rest.service.model.Country;
import nl.rainbowsheep.reisorg.rest.service.model.Trip;
import nl.rainbowsheep.reisorg.rest.service.model.TripAccommodation;
import nl.rainbowsheep.reisorg.rest.service.repository.TripRepository;
import nl.rainbowsheep.reisorg.rest.service.viewmodel.AccommodationViewmodel;
import nl.rainbowsheep.reisorg.rest.service.viewmodel.TripViewmodel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Service class for Trips
 */
@Service
public class TripService {
    
    @Autowired
    private TripRepository tripRepository;
    @Autowired
    private UserService userService;
    @Autowired
    private CustomerService customerService;
    @Autowired
    private AccommodationTypeService accommodationtypeService;
    @Autowired
    private CityService cityService;
    @Autowired
    private CountryService countryService;
    @Autowired
    private AccommodationService accommodationService;
    
    @Autowired
    private TripAccommodationService tripAccoService;
    
    public List<TripViewmodel> getAll(boolean predefined) throws Exception {
        TripViewmodel tripviewmodel = null;
        List<AccommodationViewmodel> accommodationsViewModel = null;
        AccommodationViewmodel accommodationViewmodel = null;
        List<TripViewmodel> tripsviewmodel = new ArrayList();
        
        List<Trip> trips = tripRepository.findByIsPredefined(predefined);
        
        for (Trip trip : trips) {
            accommodationsViewModel = new ArrayList();
            for (TripAccommodation tripAccommodation : trip.getTripAccommodations()) {
                accommodationViewmodel = accommodationService.findOne(tripAccommodation.getAccommodationId());
                accommodationViewmodel.setAmountOfNights(tripAccommodation.getAmountOfNights());
                accommodationViewmodel.setAccommodationIndex(tripAccommodation.getAccommodationIndex());
                accommodationsViewModel.add(accommodationViewmodel);
            }
            tripviewmodel = new TripViewmodel();
            tripviewmodel.setTrip(trip);
            tripviewmodel.setAccommodations(accommodationsViewModel);
            tripsviewmodel.add(tripviewmodel);
        }
        
        return tripsviewmodel;
    }

    /**
     * Function to return a TripViewmodel object where Country, City and
     * AccommodationType are a their Type datamember instead of a long type
     * reference.
     *
     * @param id
     * @return
     * @throws Exception
     */
    public TripViewmodel findById(long id) throws Exception {
        TripViewmodel tripviewmodel = new TripViewmodel();
        List<AccommodationViewmodel> accommodationsviewmodel = new ArrayList();
        Trip trip = null;
        
        if (tripRepository.exists(id)) {
            AccommodationViewmodel accommodationViewmodel = null;
            
            trip = tripRepository.findByIsPredefinedAndId(true, id);
            if (trip != null) {
                for (TripAccommodation tripAccommodation : trip.getTripAccommodations()) {
                    accommodationViewmodel = accommodationService.findOne(tripAccommodation.getAccommodationId());
                    accommodationViewmodel.setAmountOfNights(tripAccommodation.getAmountOfNights());
                    accommodationViewmodel.setAccommodationIndex(tripAccommodation.getAccommodationIndex());
                    accommodationsviewmodel.add(accommodationViewmodel);
                }
                tripviewmodel.setTrip(trip);
                tripviewmodel.setAccommodations(accommodationsviewmodel);
            } else {
                throw new Exception();
            }
        } else {
            throw new Exception();
        }
        return tripviewmodel;
    }
    
    public List<TripViewmodel> getBySearch(int days, int persons, List<String> searchedCountries) throws IllegalArgumentException {
        // zero persons is not allowed
        if (persons <= 0) {
            throw new IllegalArgumentException("At least 1 person is required");
        }
        
        TripViewmodel tripviewmodel = null;
        List<AccommodationViewmodel> accommodationsviewmodel = null;
        AccommodationViewmodel accommodationViewmodel = null;
        List<TripViewmodel> tripsviewmodel = new ArrayList();
        
        List<Trip> searchedTrips = new ArrayList();
        List<Long> countries = getCountriesIdsByName(searchedCountries);
        int nights = days - 1;
        
        if (!countries.isEmpty()) {
            List<Trip> requestedTrips = tripRepository.findByIsPredefinedAndMinPersonsLessThanEqualAndMaxPersonsGreaterThanEqual(true, persons, persons);
            for (Trip trip : requestedTrips) {
                for (TripAccommodation tripaccommodation : trip.getTripAccommodations()) {
                    AccommodationViewmodel acco = accommodationService.findOne(tripaccommodation.getAccommodationId());
                    for (Long countryid : countries) {
                        if (acco.getCity().getCountryId().equals(countryid)) {
                            searchedTrips.add(trip);
                        }
                    }
                    
                }
                searchedTrips = removeDuplicates(searchedTrips);
            }
            if (!searchedTrips.isEmpty()) {
                for (Trip trip : searchedTrips) {
                    accommodationsviewmodel = new ArrayList();
                    for (TripAccommodation tripAccommodation : trip.getTripAccommodations()) {
                        accommodationViewmodel = accommodationService.findOne(tripAccommodation.getAccommodationId());
                        accommodationViewmodel.setAmountOfNights(tripAccommodation.getAmountOfNights());
                        accommodationViewmodel.setAccommodationIndex(tripAccommodation.getAccommodationIndex());
                    }
                    tripviewmodel = new TripViewmodel();
                    tripviewmodel.setTrip(trip);
                    tripviewmodel.setAccommodations(accommodationsviewmodel);
                    tripsviewmodel.add(tripviewmodel);
                }
            }            
        }
        return tripsviewmodel;
    }
    
    private List<Long> getCountriesIdsByName(List<String> countrynames) {
        ArrayList<Long> countries = new ArrayList();
        //get countries by name
        for (String countryName : countrynames) {
            Country country = countryService.getCountryByName(countryName);
            if (country != null) {
                countries.add(country.getId());
            }
        }
        return countries;
    }
    
    private List<Trip> removeDuplicates(List<Trip> trips) {
        Set<Trip> uniqueTrips = new LinkedHashSet<Trip>(trips);
        //convert Set back to a list 
        return (new ArrayList<Trip>(uniqueTrips));
    }
    
    public List<Trip> findByUsernameId(long id) {
        return tripRepository.findByCustomerId(id);
    }
    
    public Trip save(Trip trip) {
        
        List<TripAccommodation> something = trip.getTripAccommodations();
        ArrayList<TripAccommodation> newlist = new ArrayList<TripAccommodation>();
        trip.setTripAccommodations(newlist);
        Trip newTrip = tripRepository.save(trip);
        
        for (TripAccommodation tripAcco : something) {
            tripAcco.setAccommodationTrip(newTrip);
            TripAccommodation savedTripAcco = tripAccoService.save(tripAcco);
            newlist.add(savedTripAcco);
        }
        newTrip.setTripAccommodations(newlist);
        return tripRepository.save(newTrip);

        //return null;
        //SORRY!!! maar ik wilde het eerst ff fixen dat je fatsoenlijk een accommodationvm krijg met amountofnights en index
    }
}
